package com.jdrx.sw.sewage.interceptor;

import com.alibaba.fastjson.JSON;
import com.jdrx.sw.sewage.common.constant.Constant;
import com.jdrx.sw.sewage.service.common.RedisService;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.dubbo.rpc.RpcContext;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;

@Intercepts({
        @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
@Component
public class BizTableInterceptor implements Interceptor {
    static Logger logger = LoggerFactory.getLogger(BizTableInterceptor.class);
    @Autowired
    private RedisService redisService;
    private String matchs;

    @SuppressWarnings("unchecked")
	@Override
    public Object intercept(Invocation invocation) throws Throwable {
        HttpServletRequest request = null;
        String db = null;
        String tenantId = null;
        if (RequestContextHolder.getRequestAttributes() != null && (request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest()) != null) {
        	tenantId = request.getHeader(Constant.TENANT_ID);
        } else if (Objects.nonNull(RpcContext.getContext().getAttachment(Constant.TENANT_ID))) {
        	tenantId = RpcContext.getContext().getAttachment(Constant.TENANT_ID);
        } else {
            return invocation.proceed();
        }
        // 解析该租户该服务数据节点
        String dataNode = redisService.getMapField(Constant.REDIS_TENANT + tenantId, "dataNode");
        if(StringUtils.isBlank(dataNode)) {
        	throw new Exception(" http header DB not found, db select error ");
        }
        Map<String, Object> dataNodeMap = (Map<String, Object>) JSON.parse(dataNode);
        db = dataNodeMap.get(Constant.APPLICATION_NAME) + "";
        logger.debug("select " + db);
        if (invocation.getTarget() instanceof RoutingStatementHandler) {

            if (StringUtils.isNotBlank(db)) {
                RoutingStatementHandler statementHandler = (RoutingStatementHandler) invocation.getTarget();
                StatementHandler delegate = (StatementHandler) getFieldValue(statementHandler, "delegate");
                BoundSql boundSql = delegate.getBoundSql();
                String sql = boundSql.getSql();
                sql = "/*!mycat:schema = " + db + " */ " + sql;
                setFieldValue(boundSql, "sql", sql);
                logger.debug(sql);
            } else {
                throw new Exception(" http header DB not found, db select error ");
            }
        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        if (target instanceof StatementHandler) {
            return Plugin.wrap(target, this);
        } else {
            return target;
        }
    }

    public static Object getFieldValue(Object obj, String fieldName) {

        if (obj == null) {
            return null;
        }

        Field targetField = getTargetField(obj.getClass(), fieldName);

        try {
            return FieldUtils.readField(targetField, obj, true);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static Field getTargetField(Class<?> targetClass, String fieldName) {
        Field field = null;

        try {
            if (targetClass == null) {
                return field;
            }

            if (Object.class.equals(targetClass)) {
                return field;
            }

            field = FieldUtils.getDeclaredField(targetClass, fieldName, true);
            if (field == null) {
                field = getTargetField(targetClass.getSuperclass(), fieldName);
            }
        } catch (Exception e) {
        }

        return field;
    }

    public static void setFieldValue(Object obj, String fieldName, Object value) {
        if (null == obj) {
            return;
        }
        Field targetField = getTargetField(obj.getClass(), fieldName);
        try {
            FieldUtils.writeField(targetField, obj, value);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void setProperties(Properties properties) {
        this.matchs = properties.getProperty("matchs", null);
        logger.info(matchs);
    }
}